; ! 分号和叹号都是注释的起始标识。源码中是用叹号开头的，但是VScode中叹号没有语法高亮，改成分号好看一点
; ! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
; ! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
; ! versions of linuxd
; ! 寻址的时候左移四位，0x3000就变成了0x30000。192k。这就是要预留给操作系统内核代码的内存空间。
; ! 对于当前版本的linux来说，192k已经够了
SYSSIZE = 0x3000
; !
; !	bootsect.s		(C) 1991 Linus Torvalds
; !                           ! ======================== step 1 ======================= 
; ! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
; ! iself out of the way to address 0x90000, and jumps there.
; !
; ! It then loads 'setup' directly after itself (0x90200), and the system
; ! at 0x10000, using BIOS interrupts. 
; !
; ! NOTE! currently system is at most 8*65536 bytes long. This should be no
; ! problem, even in the future. I want to keep it simple. This 512 kB
; ! kernel size should be enough, especially as this doesn't contain the
; ! buffer cache as in minix
; !
; ! The loader has been made as simple as possible, and continuos
; ! read errors will result in a unbreakable loop. Reboot by hand. It
; ! loads pretty fast by getting whole sectors at a time whenever possible.

; 下面这些标号是全局的，就算不用也要强制引入
.globl begtext, begdata, begbss, endtext, enddata, endbss
; 切换到正文段
.text
; 在正文段中定义一个标号
begtext:
; 切换到数据段
.data
; 在数据段中定义一个标号
begdata:
; 切换到未初始化数据段
.bss
; 在未初始化数据段中定义一个标号
begbss:
; 上面把三个段都定义在同一地址，所以本程序实际上不分段

; 切换到正文段，开始写代码
.text
; !
; ! 电脑的开机键被按下后，主板上提前写死的BIOS固件程序会把硬盘中的 0 盘 0 道 1 扇区的 512 个字节复制到内存中地址为0x7c00位置(就是写死的位置)
; ! 这个bootsect.s汇编文件在汇编后就会放在硬盘中的 0 盘 0 道 1 扇区的 512(0x200) 个字节中
; ! 定义的内存地址使用时会被左移四位，比如0x07c0会被左移成0x7c00
SETUPLEN = 4				! nr of setup-sectors
BOOTSEG  = 0x07c0			! original address of boot-sector
INITSEG  = 0x9000			! we move boot here - out of the way
SETUPSEG = 0x9020			! setup starts here
; ! 操作系统从0x10000开始。todo by聂志鹏 前0x10000空间是留给中断向量表吗？？实模式下，中断向量表在内存地址为0~1024的位置，那应该除了中断向量表，还有其他东西
SYSSEG   = 0x1000			! system loaded at 0x10000 (65536). 
; !对于当前版本的linux，他的值就是0x4000。意味着在内存中0x10000到0x40000这192k，是留给操作系统的
ENDSEG   = SYSSEG + SYSSIZE		! where to stop loading 


; ! ROOT_DEV:	0x000 - same type of floppy as boot.
; !		0x301 - first partition on first drive etc
ROOT_DEV = 0x306
; ! 告诉链接器，程序从start处开始运行
entry start
start:
	; ! 把 0x07c0 这个值复制到 ax 寄存器里。#号意思是将其当做一个立即数，而不是一个内存地址
	mov	ax,#BOOTSEG
	; ! 再将 ax 寄存器里的值复制到 ds 段基址寄存器
	; ! 段基址要先左移四位。那 0x07c0 左移四位就是 0x7c00，那这就刚好和这段代码的内存地址 0x7c00 一样了。
	; ! 所以从现在开始，段基地就是7c00.接下来的代码，都是相对于第一条代码的偏移量
	; ! 硬件上不支持直接将值传入段寄存器，所以只能先存入一般寄存器ax，再传入数据段寄存器
	mov	ds,ax
	; ! 这两行代码将es寄存器的值设置为0x9000
	mov	ax,#INITSEGf
	mov	es,ax
	; ! 设置cx寄存器的值为256（十进制）代码段寄存器
	mov	cx,#256
	; ! 将si寄存器和di寄存器中的值设置为0。 
	sub	si,si
	sub	di,di
	; ! req movw将 ds:si 处复制到 es:di 处，0x7c00 -> 0x90000，复制cx(256)次，每次复制一个字（2字节）
	; ! 也就是将0x7c00处的512字节复制到0x90000处，90000+512（0x200）=90200
	rep                      ! ======================== step 2 ======================= 
	movw                     ! and increase si,di
	; ! 段间跳转。跳转到9000+go处的代码，go是一个偏移量，意思就是执行go标签下的代码
	jmpi	go,INITSEG       ! jump to INITSEG, and start go, now cs = 0x9000 cs代码段寄存器
; ! go 就是一个标签，最终编译成机器码的时候会被翻译成一个值，这个值就是 go 这个标签在文件内的偏移地址
; ! CPU 当前正在执行的代码在内存中的位置，就是由 cs:ip 这组寄存器配合指向的，其中 cs 是基址，ip 是偏移地址
; ! 将下面三个寄存器的值都设置成9000
go:	mov	ax,cs
	mov	ds,ax
	mov	es,ax
; ! put stack at 0x9ff00.      ! 将堆栈指针 sp 指向 0x9ff00(即 0x9000:0xff00)处
	mov	ss,ax
	mov	sp,#0xFF00		; ! arbitrary value >>512

; ! load the setup-sectors directly after the bootblock.
; ! Note that 'es' is already set up.

load_setup:
	; ! 设置四个寄存器的值.现在段基地ds是9000
	mov	dx,#0x0000		! drive 0, head 0
	mov	cx,#0x0002		! sector 2, track 0
	mov	bx,#0x0200		! address = 512, in INITSEG
	mov	ax,#0x0200+SETUPLEN	! service 2, nr of sectors 将立即数516 设置到ax寄存器中
	; ! 发起 0x13 号中断，这条指令上面给 dx、cx、bx、ax 赋值都是作为这个中断程序的参数
	; ! 就是将硬盘的第 2 个扇区开始，把数据加载到内存 0x90200 处，共加载 4 个扇区.2,3,4,5扇区0x90200-0x9a000，也就是setup.s 文件
	int	0x13			! read it      ! ======================== step 3 =======================   
	; ! 复制成功，跳转到ok_load_setup，否则不断重试 
	jnc	ok_load_setup		! ok - continue
	mov	dx,#0x0000
	mov	ax,#0x0000		! reset the diskette
	int	0x13
	j	load_setup

ok_load_setup:

; ! Get disk drive parameters, specifically nr of sectors/track

	mov	dl,#0x00
	mov	ax,#0x0800		; ! AH=8 is get drive parameters
	int	0x13
	mov	ch,#0x00
	seg cs
	mov	sectors,cx
	mov	ax,#INITSEG
	mov	es,ax

; ! Print some inane message

	mov	ah,#0x03		;! read cursor pos
	xor	bh,bh
	int	0x10
	
	mov	cx,#24
	mov	bx,#0x0007		;! page 0, attribute 7 (normal)
	mov	bp,#msg1
	mov	ax,#0x1301		;! write string, move cursor
	int	0x10

;! ok, we've written the message, now
;! we want to load the system (at 0x10000)

	mov	ax,#SYSSEG
	mov	es,ax		;! segment of 0x010000
	call	read_it        ;! ======================== step 4 ======================= 
	call	kill_motor

; ! After that we check which root-device to use. If the device is
; ! defined (!= 0), nothing is done and the given device is used.
; ! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
; ! on the number of sectors that the BIOS reports currently.

	seg cs
	mov	ax,root_dev
	cmp	ax,#0
	jne	root_defined
	seg cs
	mov	bx,sectors
	mov	ax,#0x0208		;! /dev/ps0 - 1.2Mb
	cmp	bx,#15
	je	root_defined
	mov	ax,#0x021c		;! /dev/PS0 - 1.44Mb
	cmp	bx,#18
	je	root_defined
undef_root:
	jmp undef_root
root_defined:
	seg cs
	mov	root_dev,ax

; ! after that (everyting loaded), we jump to
; ! the setup-routine loaded directly after
; ! the bootblock:

	jmpi	0,SETUPSEG

; ! This routine loads the system at address 0x10000, making sure
; ! no 64kB boundaries are crossed. We try to load it as fast as
; ! possible, loading whole tracks whenever we can.
; !
; ! in:	es - starting address segment (normally 0x1000)
; !
sread:	.word 1+SETUPLEN	! sectors read of current track
head:	.word 0			! current head
track:	.word 0			! current track

read_it:
	mov ax,es
	test ax,#0x0fff
die:	jne die			! es must be at 64kB boundary
	xor bx,bx		! bx is starting address within segment
rp_read:
	mov ax,es
	cmp ax,#ENDSEG		! have we loaded all yet?
	jb ok1_read
	ret
ok1_read:
	seg cs
	mov ax,sectors
	sub ax,sread
	mov cx,ax
	shl cx,#9
	add cx,bx
	jnc ok2_read
	je ok2_read
	xor ax,ax
	sub ax,bx
	shr ax,#9
ok2_read:
	call read_track
	mov cx,ax
	add ax,sread
	seg cs
	cmp ax,sectors
	jne ok3_read
	mov ax,#1
	sub ax,head
	jne ok4_read
	inc track
ok4_read:
	mov head,ax
	xor ax,ax
ok3_read:
	mov sread,ax
	shl cx,#9
	add bx,cx
	jnc rp_read
	mov ax,es
	add ax,#0x1000
	mov es,ax
	xor bx,bx
	jmp rp_read

read_track:
	push ax
	push bx
	push cx
	push dx
	mov dx,track
	mov cx,sread
	inc cx
	mov ch,dl
	mov dx,head
	mov dh,dl
	mov dl,#0
	and dx,#0x0100
	mov ah,#2
	int 0x13
	jc bad_rt
	pop dx
	pop cx
	pop bx
	pop ax
	ret
bad_rt:	mov ax,#0
	mov dx,#0
	int 0x13
	pop dx
	pop cx
	pop bx
	pop ax
	jmp read_track

/*
 * This procedure turns off the floppy drive motor, so
 * that we enter the kernel in a known state, and
 * don't have to worry about it later.
 */
kill_motor:
	push dx
	mov dx,#0x3f2
	mov al,#0
	outb
	pop dx
	ret

sectors:
	.word 0

msg1:
	.byte 13,10 ; 定义两个字符（回车、换行）
	.ascii "Loading system ..."
	.byte 13,10,13,10

.org 508 ; 接下来的代码放在508位置处
root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55 ;!写死的，启动区最后两个字节必须是0x55和0xaa

; 三个段的结束位置
.text
endtext:
.data
enddata:
.bss
endbss:
